﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using System.Globalization;

namespace FishServe.Client.Application.Manifest
{
    internal class ModuleManifest
    {
        private ModuleManifestType _manifestType;
        private Version _version;
        private DateTime _createdDateTime;
        private string _guid;
        private ManifestModuleInfo _module;
        private List<ManifestModuleInfo> _dependentModules = new List<ManifestModuleInfo>();
        private List<ManifestRegisteredTypeInfo> _registeredTypes = new List<ManifestRegisteredTypeInfo>();
        private List<ManifestModuleInfo> _childModules = new List<ManifestModuleInfo>();

        public ModuleManifestType ManifestType
        {
            get { return _manifestType; }
        }

        public Version Version
        {
            get { return _version; }
        }

        public DateTime CreatedDateTime
        {
            get { return _createdDateTime; }
        }

        public string Guid
        {
            get { return _guid; }
        }

        public ManifestModuleInfo Module
        {
            get { return _module; }
        }

        public List<ManifestModuleInfo> DependentModules
        {
            get { return _dependentModules; }
        }

        public List<ManifestRegisteredTypeInfo> RegisteredTypes
        {
            get { return _registeredTypes; }
        }

        public List<ManifestModuleInfo> ChildModules
        {
            get { return _childModules; }
        }

        public ModuleManifest(ModuleManifestType manifestType, Version version, DateTime createdDateTime, string guid, ManifestModuleInfo module)
        {
            _manifestType = manifestType;
            _version = version;
            _createdDateTime = createdDateTime;
            _guid = guid;
            _module = module;
        }

        public ModuleManifest(XElement manifestElement)
        {
            XAttribute manifestTypeAttribute = manifestElement.Attribute("ManifestType");
            Trace.Assert(manifestTypeAttribute != null && !String.IsNullOrEmpty(manifestTypeAttribute.Value), "ManifestType attribute missing");
            _manifestType = (ModuleManifestType)Enum.Parse(typeof(ModuleManifestType), manifestTypeAttribute.Value);

            XAttribute versionAttribute = manifestElement.Attribute("Version");
            Trace.Assert(versionAttribute != null && !String.IsNullOrEmpty(versionAttribute.Value), "Manifest Version attribute missing");
            _version = new Version(versionAttribute.Value);

            XAttribute manifestGuidAttribute = manifestElement.Attribute("Guid");
            Trace.Assert(manifestGuidAttribute != null && !String.IsNullOrEmpty(manifestGuidAttribute.Value), "Manifest Guid attribute missing");
            _guid = manifestGuidAttribute.Value;

            XAttribute createdAttribute = manifestElement.Attribute("Created");
            Trace.Assert(createdAttribute != null && !String.IsNullOrEmpty(createdAttribute.Value), "Manifest Created attribute missing");
            _createdDateTime = DateTime.ParseExact(createdAttribute.Value, "yyyy-MM-dd HHHH:mm:ss", CultureInfo.InvariantCulture );

            XElement moduleElement = manifestElement.Element("Module");
            Trace.Assert(moduleElement != null, "Module element missing from Manifest element");
            _module = new ManifestModuleInfo(moduleElement);

            XElement registeredTypesElement = manifestElement.Element("RegisteredTypes");
            if (registeredTypesElement != null)
            {
                foreach (XElement registeredTypeElement in registeredTypesElement.Elements("RegisteredType"))
                {
                    XAttribute registeredNameAttribute = registeredTypeElement.Attribute("RegisteredName");

                    RegisteredTypeLifetime lifetime = RegisteredTypeLifetime.Undeclared;
                    XAttribute lifetimeAttribute = registeredTypeElement.Attribute("Lifetime");
                    if (lifetimeAttribute != null && !String.IsNullOrEmpty(lifetimeAttribute.Value))
                        lifetime = (RegisteredTypeLifetime)Enum.Parse(typeof(RegisteredTypeLifetime), lifetimeAttribute.Value);

                    XElement fromTypeElement = registeredTypeElement.Element("FromType");
                    if (fromTypeElement == null)
                        throw new Exception(String.Format("Missing FromType element in RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

                    XAttribute fromTypeNameAttribute = fromTypeElement.Attribute("TypeName");
                    if( fromTypeNameAttribute == null )
                        throw new Exception(String.Format("Missing TypeName name attribute in FromType element of RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));
                    
                    ManifestModuleTypeInfo fromModuleInfo = new ManifestModuleTypeInfo(new ManifestModuleInfo(fromTypeElement), fromTypeNameAttribute.Value);

                    XElement toTypeElement = registeredTypeElement.Element("ToType");
                    if (toTypeElement == null)
                        throw new Exception(String.Format("Missing ToType element in RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));
                    
                    XAttribute toTypeNameAttribute = toTypeElement.Attribute("TypeName");
                    if( toTypeNameAttribute == null )
                        throw new Exception(String.Format("Missing TypeName name attribute in ToType element of RegisteredType declaration in ModuleManifest '{0}' v{1}", _module.Name, _module.Version));

                    ManifestModuleTypeInfo toModuleInfo = new ManifestModuleTypeInfo(new ManifestModuleInfo(toTypeElement), toTypeNameAttribute.Value);

                    ManifestRegisteredTypeInfo registeredTypeInfo = new ManifestRegisteredTypeInfo();
                    if (registeredNameAttribute != null)
                        registeredTypeInfo.RegisteredName = registeredNameAttribute.Value;

                    registeredTypeInfo.Lifetime = lifetime;
                    registeredTypeInfo.FromTypeInfo = fromModuleInfo;
                    registeredTypeInfo.ToTypeInfo = toModuleInfo;

                    _registeredTypes.Add(registeredTypeInfo);
                    
                }
            }
        }
    }
}
